<?php
/**
 * Created by PhpStorm.
 * User: sosyuki
 * Date: 2019-01-14
 * Time: 10:30
 */

namespace Drupal\services_commerce;


trait ServicesCommerceAddOrderTrait {

  protected function commerceOrderProductAddPid($order, $product_id, $quantity) {
    if ($product = commerce_product_load($product_id)) {
      $line_item = commerce_product_line_item_new($product, $quantity);
      return $this->commerceOrderProductAdd($order, $line_item);
    }
    return FALSE;
  }

  protected function commerceOrderProductAdd($order, $line_item, $combine = TRUE) {
    // Do not add the line item if it doesn't have a unit price.
    $line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);

    if (is_null($line_item_wrapper->commerce_unit_price->value())) {
      return FALSE;
    }

    // Set the incoming line item's order_id.
    $line_item->order_id = $order->order_id;

    // Wrap the order for easy access to field data.
    $order_wrapper = entity_metadata_wrapper('commerce_order', $order);

    // Extract the product and quantity we're adding from the incoming line item.
    $product = $line_item_wrapper->commerce_product->value();
    $quantity = $line_item->quantity;

    // Invoke the product prepare event with the shopping cart order.
    rules_invoke_all('commerce_cart_product_prepare', $order, $product, $quantity);

    // Determine if the product already exists on the order and increment its
    // quantity instead of adding a new line if it does.
    $matching_line_item = NULL;

    //    If we are supposed to look for a line item to combine into...
    if ($combine) {
      // Generate an array of properties and fields to compare.
      $comparison_properties = ['type', 'commerce_product'];

      // Add any field that was exposed on the Add to Cart form to the array.
      // TODO: Bypass combination when an exposed field is no longer available but
      // the same base product is added to the cart.
      foreach (field_info_instances('commerce_line_item', $line_item->type) as $info) {
        if (!empty($info['commerce_cart_settings']['field_access'])) {
          $comparison_properties[] = $info['field_name'];
        }
      }

      // Allow other modules to specify what properties should be compared when
      // determining whether or not to combine line items.
      $clone_line_item = clone($line_item);
      drupal_alter('commerce_cart_product_comparison_properties', $comparison_properties, $clone_line_item);

      // Loop over each line item on the order.
      foreach ($order_wrapper->commerce_line_items as $delta => $matching_line_item_wrapper) {
        // Examine each of the comparison properties on the line item.
        foreach ($comparison_properties as $property) {
          // If the property is not present on either line item, bypass it.
          if (
            !isset($matching_line_item_wrapper->value()->{$property})
            && !isset($line_item_wrapper->value()->{$property})
          ) {
            continue;
          }

          // If any property does not match the same property on the incoming line
          // item or exists on one line item but not the other...
          if (
            (!isset($matching_line_item_wrapper->value()->{$property})
              && isset($line_item_wrapper->value()->{$property}))
            ||
            (isset($matching_line_item_wrapper->value()->{$property})
              && !isset($line_item_wrapper->value()->{$property}))
            ||
            $matching_line_item_wrapper->{$property}->raw() != $line_item_wrapper->{$property}->raw()
          ) {
            // Continue the loop with the next line item.
            continue 2;
          }
        }

        // If every comparison line item matched, combine into this line item.
        $matching_line_item = $matching_line_item_wrapper->value();
        break;
      }
    }
    // If no matching line item was found...
    if (empty($matching_line_item)) {
      // Save the incoming line item now so we get its ID.
      commerce_line_item_save($line_item);

      // Add it to the order's line item reference value.
      $order_wrapper->commerce_line_items[] = $line_item;
    }
    else {
      // Increment the quantity of the matching line item, update the data array,
      // and save it.
      $matching_line_item->quantity += $quantity;
      $matching_line_item->data = array_merge($line_item->data, $matching_line_item->data);

      $price = $line_item->commerce_unit_price[LANGUAGE_NONE]['0']['amount'];
      $matching_line_item->commerce_unit_price[LANGUAGE_NONE]['0']['amount'] = $price;
      $matching_line_item->commerce_unit_price[LANGUAGE_NONE]['0']['data']['components']['0']['price']['amount'] = $price;

      commerce_line_item_save($matching_line_item);

      // Update the line item variable for use in the invocation and return value.
      $line_item = $matching_line_item;
    }

    // Save the updated order.
    commerce_order_save($order);

    // Invoke the product add event with the newly saved or updated line item.
    rules_invoke_all('commerce_cart_product_add', $order, $product, $quantity, $line_item);

    // Return the line item.
    return $line_item;
  }
}
